
Imports System.IO
Imports std
Imports streamhandle

Namespace streamhandle
    Public Module streamhandle2
    End Module
End Namespace

Public Module _streamhandle2
    Public Delegate Function readsingleDelegate(ByVal d As Byte) As Boolean
    Public Delegate Function readmultiDelegate(ByVal d() As Byte, ByVal start As Int64, ByVal count As Int64) As Boolean

    Public Function traversalstream(ByVal s As Stream, ByVal readsingle As readsingleDelegate, _
                                    ByVal readmulti As readmultiDelegate, _
                                    Optional ByVal offsetInStream As Int64 = npos, _
                                    Optional ByVal length As Int64 = npos, _
                                    Optional ByVal retrytimes As Int32 = npos, _
                                    Optional ByVal starttime As Int64 = npos, _
                                    Optional ByVal holdtime As Int64 = maxInt64, _
                                    Optional ByVal sleeptime As Int64 = npos) As Int64
        On Error Resume Next

        Dim rtn As ternary = Nothing
        rtn = initial_traversal(s, offsetInStream, length, starttime, holdtime)
        If rtn.notunknown() Then
            Return rtn.true_()
        End If

        If retrytimes = npos AndAlso sleeptime > 0 Then
            retrytimes = holdtime \ sleeptime
        End If
        If retrytimes < 0 Then
            retrytimes = streamhandle.defaultRetryTimes
        End If

        Dim buff As bytes = Nothing
        buff = New bytes()
        buff.size = readstep
        Dim readcount As Int64
        readcount = 0
        Do
            Dim b As Int32
            Dim thistime As Int64
            Dim expect As Int64
            If length = npos Then
                expect = readstep
            Else
                expect = min(length - readcount, readstep)
            End If
            thistime = 0
            thistime = s.Read(buff, 0, expect)
            If thistime > 0 Then
                If Not readmulti(buff, 0, thistime) Then
                    retrytimes -= 1
                End If
                readcount += thistime
            End If
            If thistime < expect Then
                b = s.ReadByte()
                If b = npos Then
                    'if length <=0, readcount cannot be < length
                    If readcount < length Then
                        retrytimes -= 1
                        If sleeptime > 0 Then
                            sleep(sleeptime)
                        End If
                    Else
                        Exit Do
                    End If
                Else
                    If Not readsingle(Convert.ToByte(b)) Then
                        retrytimes -= 1
                    End If
                    readcount += 1
                End If
            End If
        Loop Until retrytimes <= 0 OrElse readcount = length OrElse _
                   timeoutms(starttime, holdtime) <= 0
        buff.clear()
        Return readcount
    End Function

#If RETIRED Then
    Public Function readstream(ByRef s As Stream, ByRef d() As Byte _
                                , Optional ByVal start As Int64 = npos _
                                , Optional ByVal length As Int64 = npos _
                                , Optional ByVal retrytimes As Int32 = npos _
                                , Optional ByVal starttime As Int64 = npos _
                                , Optional ByVal holdtime As Int64 = maxInt64 _
                                , Optional ByVal sleeptime As Int64 = npos) As Int64
        Dim readcount As Int64
        Dim buff As bytes = Nothing
        readcount = readstream(s, buff, start, length, retrytimes, starttime, holdtime, sleeptime)
        assert(Not buff Is Nothing, "buff is nothing after readstream.")
        d = buff.data

        Return readcount
    End Function
#End If

    Public Function readstream(ByRef s As Stream, ByRef buff As bytes, _
                               Optional ByVal offsetInStream As Int64 = npos, _
                               Optional ByVal length As Int64 = npos, _
                               Optional ByVal retrytimes As Int32 = npos, _
                               Optional ByVal starttime As Int64 = npos, _
                               Optional ByVal holdtime As Int64 = maxInt64, _
                               Optional ByVal sleeptime As Int64 = npos) As Int64
        Dim v As vector(Of Byte) = Nothing
        If length > 0 Then
            'if already know the length required, just set the vector size be the same as it,
            'so can save time from re-alloc memory
            v = New vector(Of Byte)(length)
        Else
            v = New vector(Of Byte)()
        End If
        Dim readcount As Int64
        readcount = traversalstream(s, AddressOf v.push_back, AddressOf v.push_back, offsetInStream, _
                                    length, retrytimes, starttime, holdtime, sleeptime)

        If Not buff Is Nothing Then
            buff.clear()
            buff.copy(v.data())
        Else
            buff = New bytes(v.data())
        End If
        'deallocate the memory as quick as possible
        v.clear()
        v.reserve()

        Return readcount
    End Function

    Private Class syncstreamHandler
        Public o As Stream

        Public Function writebyte(ByVal b As Byte) As Boolean
            assert(Not o Is Nothing, "output stream is nothing before write.")
            'assert(o.CanWrite, "output stream cannot write.")
            If Not o.CanWrite Then
                Return False
            Else
                o.WriteByte(b)
                Return True
            End If
        End Function

        Public Function write(ByVal d() As Byte, ByVal start As Int64, ByVal count As Int64) As Boolean
            assert(Not o Is Nothing, "output stream is nothing before write.")
            'assert(o.CanWrite, "output stream cannot write.")
            If Not o.CanWrite Then
                Return False
            Else
                o.Write(d, start, count)
                Return True
            End If
        End Function

        Public Sub New(ByVal s As Stream)
            o = s
        End Sub
    End Class

    Public Function syncstream(ByVal i As Stream, ByVal o As Stream, _
                               Optional ByVal offsetInStream As Int64 = npos, _
                               Optional ByVal length As Int64 = npos, _
                               Optional ByVal retrytimes As Int32 = npos, _
                               Optional ByVal starttime As Int64 = npos, _
                               Optional ByVal holdtime As Int64 = maxInt64, _
                               Optional ByVal sleeptime As Int64 = npos) As Int64
        If o Is Nothing OrElse Not o.CanWrite Then
            raiseError("cannot write to output stream.", errorHandle.errorType.warning)
            Return npos
        End If

        Dim handler As syncstreamHandler = Nothing
        handler = New syncstreamHandler(o)
        Return traversalstream(i, AddressOf handler.writebyte, AddressOf handler.write, offsetInStream, _
                               length, retrytimes, starttime, holdtime, sleeptime)
    End Function

#If CANNOT_DETERMINE Then
    Friend Class streammoveHandler
        Public o As Stream
        Public start As Int64

        Public Sub writebyte(ByVal b As Byte)
            assert(Not o Is Nothing, "output stream is nothing before write.")
            assert(o.CanWrite, "output stream cannot write.")
            o.Position = start
            o.WriteByte(b)
            start = o.Position
        End Sub

        Public Sub write(ByVal d() As Byte, ByVal start As Int64, ByVal count As Int64)
            assert(Not o Is Nothing, "output stream is nothing before write.")
            assert(o.CanWrite, "output stream cannot write.")
            o.Position = start
            o.Write(d, start, count)
            start = o.Position
        End Sub

        Public Sub New(ByVal s As Stream, ByVal movestart As Int64)

        End Sub
    End Class
#End If

    Public Function streammove(ByVal s As Stream,
                               ByVal origStart As Int64,
                               ByVal destStart As Int64,
                               Optional ByVal length As Int64 = npos,
                               Optional ByVal retrytimes As Int32 = npos,
                               Optional ByVal starttime As Int64 = npos,
                               Optional ByVal holdtime As Int64 = maxInt64,
                               Optional ByVal sleeptime As Int64 = npos) As Int64
        assert(Not s Is Nothing, "input stream is nothing.")
        assert(s.CanRead(), "input stream cannot read.")
        assert(s.CanWrite(), "input stream cannot write.")
        assert(s.CanSeek(), "input stream cannot seak.")
        Dim b As bytes = Nothing
        Dim readcount As Int64
        readcount = readstream(s, b, origStart, length, ifoper(retrytimes = npos, npos, retrytimes >> 1), _
                               starttime, holdtime >> 1, sleeptime)
        s.Position = destStart
        If Not writestream(s, b, 0, readcount, ifoper(retrytimes = npos, npos, retrytimes >> 1), _
                           starttime, holdtime >> 1, sleeptime) Then
            readcount = npos
        End If
        b.clear()
        Return readcount
    End Function

End Module
